package CPM;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.StringTokenizer;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopScoreDocCollector;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;


public class CPMImplementation {
	static int Count=0;
	private static String[] Stopwords;
	
	String Fname="Config.properties";
	static String Per_Word_Index;
	static String Per_Entity_Index;

	public static void main(String args[]) throws IOException, ParseException,
	SQLException {
		Configuration conf=new Configuration();

		Stopwords = getStopwords();
		String abstract_file=conf.getAbstractFile();
		Per_Word_Index=conf.getPerWordIndex();
		Per_Entity_Index=conf.getPerEntityIndex();
		
		ArrayList<String> tokenset = getAbstractTokens(abstract_file);
		
		//System.out.println(tokenset.toString());
		
		long start = System.currentTimeMillis();
		ArrayList<String> matched_words=matchList(tokenset);
		getMatches(matched_words);
		
		long end = System.currentTimeMillis();
		System.out.println(end - start);
	}
	
	
	public static ArrayList<String> matchList(ArrayList<String> tokenset){
		ArrayList<String>matched_words=new ArrayList<>();
		for (int each_token = 0; each_token < tokenset.size(); each_token++) {
			String get = null;
			try {
				get = perWordCPM(tokenset.get(each_token), 0.7);
			} catch (IOException | ParseException | SQLException e) {
				e.printStackTrace();
			}
			if (get.equals("NSW")) {
				matched_words.add("$");
			} else
				matched_words.add(get);
		}
		return matched_words;
		
	}
	
	public static ArrayList<String> getAbstractTokens(String abstract_file){
		FileReader fr = null;
			
		try {
			fr = new FileReader(
					abstract_file);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		BufferedReader fbr = new BufferedReader(fr);
		String word;
		String token = null;
		ArrayList<String> tokenset = new ArrayList<String>();
		try {
			word = fbr.readLine();
	
		
		while (word != null) {

			StringTokenizer st = new StringTokenizer(word);

			while (st.hasMoreTokens()) {
				token = st.nextToken();
				if (isStopWord(Stopwords,token) == false)
					tokenset.add(token);
			}
			word = fbr.readLine();

		}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return tokenset;
	}
	
	
	public static Connection connection(){
		Connection con = null;
		String url = "jdbc:mysql://localhost:3306/";
		String db = "cpmnew";
		String driver = "com.mysql.jdbc.Driver";

		try {
			Class.forName(driver);
			con = DriverManager.getConnection(url + db, "root", "");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return con;
	}


	public static void getMatches(ArrayList<String>matched_words) throws SQLException, IOException, ParseException{
		Connection con=connection();
		PreparedStatement statement;
		ResultSet rs;
		Set<Integer> out = new LinkedHashSet<Integer>();
		String word = null;
		String token=null;
		int length=0;
		while(length<matched_words.size()){
				token = matched_words.get(length);
						
				if (token.equals("$")==true) {
					length++;
					token = matched_words.get(length);
					// System.out.println("here");
				} else {
					word=token;

					System.out.println(word);
					if (length<matched_words.size()-1){
						length++;
					token = matched_words.get(length);
					}
					while (token.equals("$")==false) {
						word = word + " " + token;
						 System.out.println(word+" k");
						if (length<matched_words.size()-1){
							length++;
						token = matched_words.get(length);
						}

					}
					out=perEntityCPM(word, 0.7);
					word = null;
					Iterator<Integer> itr=out.iterator();
					
					while(itr.hasNext()){
						
					String sql3 = "SELECT * FROM bioset WHERE number='"
							+ itr.next() + "'";
					statement = con.prepareStatement(sql3);
					rs = statement.executeQuery(sql3);
					while (rs.next()) {
						String ans = rs.getString("name");
						if (ans != null)
							System.out.println(ans);

					}
				}
					if (length<matched_words.size()-1)
						length++;
					

				}

			}
		
	}
	

	public static int[] perWordIndexSearch(String input) throws IOException,
	ParseException {

		StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_34);

		FSDirectory index = FSDirectory.open(new File(
				Per_Word_Index));

		Query q = new QueryParser(Version.LUCENE_34, "name", analyzer)
		.parse(input);

		int hitsPerPage = 355000;
		IndexReader indexReader = IndexReader.open(index);
		IndexSearcher searcher = new IndexSearcher(indexReader);

		TopScoreDocCollector collector = TopScoreDocCollector.create(
				hitsPerPage, true);
		searcher.search(q, collector);
		ScoreDoc[] hits = collector.topDocs().scoreDocs;

		// 4. display results
		int[] matches = new int[hits.length];
		for (int current_match = 0; current_match < hits.length; ++current_match) {
			int docId = hits[current_match].doc;
			Document d = searcher.doc(docId);
			String str = d.get("id");
			int ind = Integer.parseInt(str);

			matches[current_match] = ind;

		}
		return matches;

	}
	
	public static int[] perEntityIndexSearch(String entity) throws IOException,
	ParseException {

		StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_34);

		FSDirectory index = FSDirectory.open(new File(
				Per_Entity_Index));

		Query q = new QueryParser(Version.LUCENE_34, "name", analyzer)
		.parse(entity);

		int hitsPerPage = 355000;
		IndexReader indexReader = IndexReader.open(index);
		IndexSearcher searcher = new IndexSearcher(indexReader);

		TopScoreDocCollector collector = TopScoreDocCollector.create(
				hitsPerPage, true);
		searcher.search(q, collector);
		ScoreDoc[] hits = collector.topDocs().scoreDocs;

		// 4. display results
		// System.out.println("Found " + hits.length + " hits.");
		int[] matches = new int[hits.length];
		for (int current_match = 0; current_match < hits.length; ++current_match) {
			int docId = hits[current_match].doc;
			Document d = searcher.doc(docId);
			String str = d.get("id");
			int ind = Integer.parseInt(str);

			matches[current_match] = ind;

		}
		return matches;

	}
	
	@SuppressWarnings("null")
	public static String[] getStopwords(){
		FileReader fr = null;
		String[] stop_word_list = null;
		int count=0;
		try {
			fr = new FileReader("//stopwords.txt");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		BufferedReader fbr=new BufferedReader(fr);
		try {
			String stopword = fbr.readLine();
			while(stopword!=null){
				stop_word_list[count]=stopword;
				count++;
				stopword=fbr.readLine();

			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return stop_word_list;
	
	}
	
	public static boolean isStopWord(String[] Stopwords, String str)// exclude stop words from
	// dictionary
	{
		return Arrays.asList(Stopwords).contains(str);

	}

	public static int calcTau(int given_length, double alpha) {

		int token_length = 0;
		if (given_length > 23)
			token_length = 23;
		else
			token_length = given_length;
		// System.out.println(y);
		double t = alpha * token_length;
		// System.out.println(t);
		int tau = (int) Math.floor(t);
		// System.out.println(tau);

		return tau;

	}

	public static String[] gettrigram(String input) {
		String temp = input.concat("$$");
		String token = "$$".concat(temp);// add $$ before and after word
		int length = token.length();

		String trigram[] = new String[length - 2];
		char[] c = token.toCharArray();// each word to character array

		for (int l = 0; l < (length - 2); l++) {// create trigrams
			char[] cn = { c[l], c[l + 1], c[l + 2] };
			trigram[l] = new String(cn);

		}
		return trigram;

	}

	public static String perWordCPM(String entity, double alpha) throws IOException,
	ParseException, SQLException {
		Connection con =connection();

		PreparedStatement statement;
		ResultSet rs;

		int len = entity.length() + 2;

		int tau = calcTau(len, alpha);

		String trigram[] = gettrigram(entity);

		HashMap<Integer, Integer> hm = new HashMap<Integer, Integer>();

		int found[] = null;
		int number;
		int trigramid = 0;

		for (int k = 0; k <= (len - tau); k++) {

			found = perWordIndexSearch(trigram[k]);

			for (int j = 0; j < found.length; j++) {
				String sql1 = "SELECT * FROM idset WHERE trigram_id=" + found[j];
				statement = con.prepareStatement(sql1);
				rs = statement.executeQuery(sql1);
				while (rs.next()) {
					number = rs.getInt("number_id");
					trigramid = rs.getInt("trigram_id");

					if (hm.containsKey(number)) {
						Count = hm.get(number);
						Count++;
						hm.put(number, Count);
					} else {

						hm.put(number, 1);
					}
				}

			}
		}

		for (int k = (len - tau + 1); k < len; k++) {
			String sql2 = "SELECT * FROM trigramset WHERE trigram='"
					+ trigram[k] + "'";
			statement = con.prepareStatement(sql2);
			rs = statement.executeQuery(sql2);
			while (rs.next()) {
				trigramid = rs.getInt("trigram_id");
			}

			for (Iterator<Integer> itr = hm.keySet().iterator(); itr.hasNext();) {

				int next = itr.next();

				String sql3 = "SELECT * FROM idset WHERE trigram_id='"
						+ trigramid + "'AND number_id='" + next + "'";
				statement = con.prepareStatement(sql3);
				rs = statement.executeQuery(sql3);
				while (rs.next()) {
					number = rs.getInt("number_id");

					Count = hm.get(next);
					Count = Count + 1;
					;

					hm.put(next, Count);
					// System.out.println(count);

				}

				// System.out.println(count);
				if (Count >= tau) {
					return entity;
				}

			}
		}
		return "NSW";

	}
	
	
	public static Set<Integer> perEntityCPM(String word, double alpha) throws SQLException,
	IOException, ParseException {
		Connection con = connection();
		PreparedStatement statement;
		ResultSet rs;
		Set<Integer> ar = new LinkedHashSet<Integer>();

		int len = word.length() + 2;

		int tau = calcTau(len, alpha);

		String trigram[] = gettrigram(word);

		HashMap<Integer, Integer> hm = new HashMap<Integer, Integer>();

		int found[] = null;
		int number = 0;
		int trigramid = 0;

		for (int k = 0; k <= (len - tau); k++) {

			found = perEntityIndexSearch(trigram[k]);

			for (int j = 0; j < found.length; j++) {
				String sql = "SELECT * FROM fullbioset WHERE full_id="
						+ found[j];
				statement = con.prepareStatement(sql);
				rs = statement.executeQuery(sql);
				while (rs.next()) {
					number = rs.getInt("set_id");
					trigramid = rs.getInt("full_id");

					if (hm.containsKey(number)) {
						Count = hm.get(number);
						Count++;
						hm.put(number, Count);
					} else {

						hm.put(number, 1);
					}
				}

			}
		}

		for (int k = (len - tau + 1); k < len; k++) {
			String sql1 = "SELECT * FROM fulltrigramset WHERE fulltrigram='"
					+ trigram[k] + "'";
			statement = con.prepareStatement(sql1);
			rs = statement.executeQuery(sql1);
			while (rs.next()) {
				trigramid = rs.getInt("full_id");
			}

			
			for (Iterator<Integer> itr = hm.keySet().iterator(); itr.hasNext();) {

				int next = itr.next();

				String sql2 = "SELECT * FROM fullbioset WHERE full_id='"
						+ trigramid + "'AND set_id='" + next + "'";
				statement = con.prepareStatement(sql2);
				rs = statement.executeQuery(sql2);
				while (rs.next()) {
					while(true){
					number = rs.getInt("set_id");

					Count = hm.get(next);
					Count = Count + 1;

					hm.put(next, Count);
					// System.out.println(count);

					// System.out.println(count);
					if (Count >= tau) {
						
						ar.add(number);
						break;

					}

					else if ((Count + (len - k - 1)) < tau) {
						break;
					}
					}
				}
			}
		}
		return ar;
	}

}
